package org.tgit.gateway.route;
/**
 * 
* @ClassName: StaticRoute   
* @Description: TODO(这里用一句话描述这个类的作用)   
* @author fanzaijun 
* @date 2018年8月8日 下午2:39:50   
*
 */

import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponentsBuilder;
import org.tgit.gateway.common.ApiConstants;
import org.tgit.gateway.common.CommonUtil;
import org.tgit.gateway.domain.ApiGateWayInfo;

import com.netflix.config.ConfigurationManager;

import reactor.core.publisher.Mono;
/**
* 路由表
* @ClassName: RouteTable   
* @Description: TODO(这里用一句话描述这个类的作用)   
* @author fanzaijun 
* @date 2018年8月8日 下午2:45:03   
*
 */
@Component
public class RouteConfig implements ApplicationEventPublisherAware {

	//路由写入通道
	@Autowired
    private RouteDefinitionWriter     routeDefinitionWriter;
	//限流方式自定义
	@Value("${limit.case}") 
	private String limitCase;
	//路由发布通道
    private ApplicationEventPublisher publisher;
	
	@Override
	public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
		this.publisher=publisher;
	}
	
	//addRoute-by host,uri
	@SuppressWarnings("rawtypes")
	public String addRouteHostUri(ApiGateWayInfo apiGateWayInfo) {
		//路由自定义
		RouteDefinition routedefinition = new RouteDefinition();
		//deploy
		/*******************************谓词***************************/
		
		List<PredicateDefinition> predicates=new ArrayList<PredicateDefinition>();
		//谓词自定义
		PredicateDefinition predicatePath = new PredicateDefinition();
		PredicateDefinition predicateMethod = new PredicateDefinition();
		//谓词参数map集合
		Map<String, String> predicateParamsPath = new HashMap<>(8);
		Map<String, String> predicateParamsMethod = new HashMap<>(8);
		//谓词是Path
		predicatePath.setName(ApiConstants.ROUTE_CHANNE.PATH);
		predicateParamsPath.put("pattern", apiGateWayInfo.getAccessUri());
		predicateParamsPath.put("pathPattern", apiGateWayInfo.getAccessUri());
		predicatePath.setArgs(predicateParamsPath);
		predicates.add(predicatePath);
		//谓词是method
		predicateMethod.setName(ApiConstants.ROUTE_CHANNE.METHOD);
		predicateParamsMethod.put("method", apiGateWayInfo.getMethod().toUpperCase());
		predicateMethod.setArgs(predicateParamsMethod);
		predicates.add(predicateMethod);
		//路由嵌入谓词
		routedefinition.setPredicates(predicates);
		
		/*****************************过滤器*****************************/
		
		List<FilterDefinition> filters=new ArrayList<FilterDefinition>();
		//过滤器RedisLimit限流操作
		FilterDefinition filterDefinitionRedisLimit = new FilterDefinition();
		//过滤器RedisLimit参数map集合
		Map<String,String> filterRedisLimitParams = new HashMap<>(8);
		//熔断器配置
		FilterDefinition filterDefinitionHystrix=new FilterDefinition();
		Map<String,String> filterHystrixParams=new HashMap<>();
		//路由ID
		routedefinition.setId(CommonUtil.genLogid());
		//过滤器RedisLimit：
		//参数说明： redis-rate-limiter.replenishRate:允许用户每秒处理多少个请求-即用户可以发出多少个请求
		//       redis-rate-limiter.burstCapacity:令牌桶的容量，允许在一秒钟内完成的最大请求数
		//       key-resolver：使用SpEL按名称引用bean
		filterDefinitionRedisLimit.setName(ApiConstants.ROUTE_CHANNE.NET_LIMIT);
		filterRedisLimitParams.put("redis-rate-limiter.replenishRate",String.valueOf(apiGateWayInfo.getMaxConcurrency()));
		filterRedisLimitParams.put("redis-rate-limiter.burstCapacity",String.valueOf(apiGateWayInfo.getMaxConcurrency()*2));
		switch (limitCase.toLowerCase()) {
		case "ip":
			filterRedisLimitParams.put("key-resolver","#{@ipKeyResolver}");
			break;
		case "user":
			filterRedisLimitParams.put("key-resolver","#{@userKeyResolver}");
			break;
		case "api":
			filterRedisLimitParams.put("key-resolver","#{@apiKeyResolver}");
			break;
		default:
			//没有设置就是ip
			filterRedisLimitParams.put("key-resolver","#{@ipKeyResolver}");
			break;
		}
		filterDefinitionRedisLimit.setArgs(filterRedisLimitParams);
		filterDefinitionHystrix.setName("Hystrix");
		filterHystrixParams.put("name","forwardUri");
		filterHystrixParams.put("fallbackUri","forward:/rest/gateway/manager/error/114");
		filterDefinitionHystrix.setArgs(filterHystrixParams);
		filters.add(filterDefinitionHystrix);
		filters.add(filterDefinitionRedisLimit);
		//路由嵌入过滤器
		routedefinition.setFilters(filters);
		
		Iterator it=apiGateWayInfo.getDeployHost().iterator();
		String host="";
		while(it.hasNext()) {
			host=host+","+(String) it.next();
		}
		ConfigurationManager.getConfigInstance().setProperty(routedefinition.getId()+".ribbon.listOfServers", host.toString().substring(1));       
		ConfigurationManager.getConfigInstance().setProperty(routedefinition.getId()+".ribbon.NFLoadBalancerRuleClassName", "com.netflix.loadbalancer.RandomRule");       
		/***********************************uri*************************************/
		//路由嵌入部署地址
		routedefinition.setUri(URI.create("lb://"+routedefinition.getId()));
		//subscribe一定要，订阅主题
		routeDefinitionWriter.save(Mono.just(routedefinition)).subscribe();
		//发布事件，更新路由操作
		this.publisher.publishEvent(new RefreshRoutesEvent(this));
		return "success";
	}

	//ip限流
	@Bean
	@ConditionalOnProperty(value="limit.case",havingValue="ip")
	public KeyResolver ipKeyResolver() {
	    return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
	}
	
	//用户限流
	@Bean
	@ConditionalOnProperty(value="limit.case",havingValue="user")
	KeyResolver userKeyResolver() {
	    return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
	}
	
	//接口限流
	@Bean
	@ConditionalOnProperty(value="limit.case",havingValue="api")
	KeyResolver apiKeyResolver() {
	    return exchange -> Mono.just(exchange.getRequest().getPath().value());
	}
}
